package com.opennews.openplatform.myspringbootmvccore.helper;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.opennews.openplatform.myspringbootcore.common.constant.HttpRequestContentType;
import com.opennews.openplatform.myspringbootcore.common.constant.HttpResponse;
import com.opennews.openplatform.myspringbootcore.dto.ApiResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;

import java.io.IOException;
import java.time.Instant;
import java.util.Map;

public class HttpResponseHelper {
    private HttpServletResponse response;

    public HttpResponseHelper(HttpServletResponse response) {
        this.response = response;
    }

    /**
     * Sends a successful response with the given payload.
     *
     * @param payload the response payload
     * @return ResponseEntity with OK status and payload
     */
    public <T> ResponseEntity<ApiResponse<T>> getSuccessResponse(T payload) {
        return getDataResponse(true, HttpStatus.OK, payload);
    }

    /**
     * Sends a successful response with the given http status.
     */
    public <T> ResponseEntity<ApiResponse<T>> getSuccessResponse(HttpStatus httpStatus) {
        return getDataResponse(true, httpStatus, null);
    }

    /**
     * Sends a successful response with default HttpStatus.NO_CONTENT.
     */
    public <T> ResponseEntity<ApiResponse<T>> getSuccessResponse() {
        return getDataResponse(true, HttpStatus.NO_CONTENT, null);
    }

    /**
     * Sends a successful response with the given http status and payload.
     */
    public <T> ResponseEntity<ApiResponse<T>> getSuccessResponse(HttpStatus httpStatus, T payload) {
        return getDataResponse(true, httpStatus, payload);
    }

    public <T> ResponseEntity<ApiResponse<T>> getErrorResponse(HttpStatus httpStatus, T payload) {
        return getDataResponse(false, httpStatus, payload);
    }

    /**
     * Sends a response with the specified HTTP status and payload.
     *
     * @param httpStatus the HTTP status to send
     * @param payload    the response payload
     * @return ResponseEntity with the specified status and payload
     */
    public <T> ResponseEntity<ApiResponse<T>> getDataResponse(boolean success, HttpStatus httpStatus, T payload) {
        ApiResponse<T> response = new ApiResponse<>(success, payload);

        return ResponseEntity.status(httpStatus).body(response);
    }

    /**
     * Sends http error to client as request response.
     * Example:
     * {
     * success: false,
     * timestamp: 1642586379,
     * status: 401,
     * error: "UNAUTHORIZED",
     * message: "ERROR_USER_ACCOUNT_NOT_AUTHORIZED",
     * path: "/family-expense/api/user"
     * }
     *
     * @param httpStatus: Http status.
     * @param error:      String to identify error.
     * @param message:    String of more specific message.
     * @param path:       String of request url path.
     */
    public void sendErrorResponse(int httpStatus, String error, String message, String path) {
        response.resetBuffer();
        response.setStatus(httpStatus);
        response.addHeader(HttpResponse.HEADER_ERROR_KEY, message);
        response.setHeader(HttpHeaders.CONTENT_TYPE, HttpRequestContentType.JSON);

        try {
            response.getOutputStream().print(new ObjectMapper().writeValueAsString(Map.of(
                    "success", false,
                    "timestamp", Instant.now().getEpochSecond(),
                    "status", httpStatus,
                    "error", error,
                    "message", message,
                    "path", path
            )));

            // Marks response as committed -- if we don't do this the request will go through normally!
            response.flushBuffer();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
